package dir_sync;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class CopyThread extends Thread {
    private static final String STATUS_LABEL_FORMAT = "统计需要复制的文件: %d";
    private static final String COPY_FILE_LOG_FORMAT = "复制文件：%s --> 到目录 %s";
    private static final String COPY_DIR_LOG_FORMAT = "复制目录：%s --> 到目录 %s";
    private static final String ERROR_LOG_FORMAT = "错误：%s - %s";

    private final List<File> srcChangedFiles;
    private final String dir_a;
    private final String dir_b;
    private final JLabel statusLabel;
    private final JTextArea logTextArea;
    private final JProgressBar progressBar;
    private final JButton compareButton;
    private boolean isStop = false;
    private final JButton startSyncButton;
    private final JButton syncCopyButton;

    public CopyThread(String name, List<File> va, String dira, String dirb,
                      JLabel clb, JTextArea cta, JProgressBar prob,
                      JButton compareButton, JButton startSyncButton, JButton syncCopyButton) {
        super(name);
        this.srcChangedFiles = new ArrayList<>(va);
        this.dir_a = dira;
        this.dir_b = dirb;
        this.statusLabel = clb;
        this.logTextArea = cta;
        this.progressBar = prob;
        this.compareButton = compareButton;
        this.startSyncButton = startSyncButton;
        this.syncCopyButton = syncCopyButton;
    }

    @Override
    public void run() {
        // TODO: 停止任务
        startSyncButton.setEnabled(false);
        try {
            doCopy(statistics(srcChangedFiles), dir_a, dir_b);
        } catch (Exception e) {
            this.logTextArea.append(String.format(ERROR_LOG_FORMAT, e.getMessage(), dir_a));
        } finally {
            startSyncButton.setEnabled(true);
        }
    }

    private List<File> statistics(List<File> files) {
        statusLabel.setText(String.format(STATUS_LABEL_FORMAT, files.size()));
        List<File> result = new ArrayList<>();
        for (File file : files) {
            if (isStop) {
                break;
            }

            if (file.isDirectory()) {
                try {
                    processDirectory(file, result);
                } catch (Exception e) {
                    logError("处理目录时发生错误", file.getAbsolutePath(), e);
                }
            } else {
                result.add(file);
            }
        }
        return result;
    }

    private void processDirectory(File directory, List<File> result) {
        if (directory == null) {
            throw new IllegalArgumentException("目录不能为null");
        }

        Queue<File> queue = new LinkedList<>();
        queue.offer(directory);

        while (!queue.isEmpty()) {
            if (isStop) {
                break;
            }

            File currentDir = queue.poll();
            File[] files = currentDir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (isStop) {
                        break;
                    }

                    if (file.isDirectory()) {
                        queue.offer(file);
                    } else {
                        result.add(file);
                    }
                }
            } else {
                result.add(currentDir);
            }
        }
    }


    private void doCopy(List<File> files, String dir1, String dir2) {
        statusLabel.setText("开始复制文件。");
        logTextArea.setText("");

        int errorCount = 0;
        int totalFiles = files.size();
        progressBar.setValue(0);
        progressBar.setMaximum(totalFiles);

        for (File file : files) {
            if (isStop) {
                break;
            }

            try {
                Path targetPath = Paths.get(file.getPath().replace(dir1, dir2));
                if (file.isFile()) {
                    copyFile(file.toPath(), targetPath);
                    logCopyFile(file, dir2);
                } else if (file.isDirectory()) {
                    copyDirectory(file, targetPath.toFile());
                    logCopyDirectory(file, dir2);
                }
                progressBar.setValue(progressBar.getValue() + 1);
            } catch (IOException e) {
                logError("复制文件或目录时发生错误", file.getPath(), e);
                errorCount++;
            }
        }

        statusLabel.setText("文件同步完毕！");
        JOptionPane.showMessageDialog(null, String.format(
                "复制目录及文件完毕！\n共有：%d 个文件和目录需要同步。同步结果为：%d 个。\n文件复制错误：%d 个",
                totalFiles, totalFiles - errorCount, errorCount));
    }

    private void copyFile(Path source, Path target) throws IOException {
        // 判断一下target路径是否存在，如果不存在，则创建所有目录
        if (!Files.exists(target.getParent())) {
            logMessage("目录不存在，开始创建目录: " + target.getParent());
            Files.createDirectories(target.getParent());
        }

        if (Files.exists(source)) {
            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
        } else {
            logMessage("源文件不存在: " + source);
        }
    }

    private void copyDirectory(File source, File target) throws IOException {
        if (!target.exists()) {
            if (target.mkdirs()) {
                logMessage("ERROR: 创建目录失败: " + target.getAbsolutePath());
            }
        }
        File[] files = source.listFiles();
        if (files == null) {
            return;
        }
        for (File child : files) {
            if (isStop) {
                break;
            }

            if (child.isDirectory()) {
                copyDirectory(child, new File(target, child.getName()));
            } else {
                copyFile(child.toPath(), new File(target, child.getName()).toPath());
            }
        }
    }

    private void logCopyFile(File file, String targetDir) {
        String logMessage = String.format(COPY_FILE_LOG_FORMAT, file, targetDir);
        logTextArea.append(logMessage + "\n");
        statusLabel.setText(logMessage);
    }

    private void logCopyDirectory(File dir, String targetDir) {
        String logMessage = String.format(COPY_DIR_LOG_FORMAT, dir, targetDir);
        logTextArea.append(logMessage + "\n");
        statusLabel.setText(logMessage);
    }

    private void logError(String message, String path, Exception e) {
        String logMessage = String.format(ERROR_LOG_FORMAT, message, path);
        logTextArea.append(logMessage + "\n");
        e.printStackTrace();
    }

    private void logMessage(String msg) {
        logTextArea.append(msg + "\n");
        statusLabel.setText(msg);
    }

    public void stopRun() {
        isStop = true;
    }
}
